# coding:utf-8
import functools
from threading import Thread
import os

from flask import Blueprint, current_app, request, jsonify, make_response

from base_tool import to_md5,timestamp_13,is_domain,WK_PATH,shell
from current_app_tool import CONFIG,Daemon,App,Log,Q_01,F_01_status
from config import Caddy_config
# 新建蓝图，用于注册管理页面
route_of_admin = Blueprint("route_of_admin", __name__)


def cross_origin(function):
    """
    装饰器：用于增加跨域头部
    Param:
        @function 被装饰的函数
    """
    # Access-Control-Allow-Origin: *
    # Access-Control-Allow-Origin: <origin>
    @functools.wraps(function)
    def decorator(*args, **kwargs):
        if request.method == 'OPTIONS':
            response = make_response('')
        else:
            response = make_response(function(*args,**kwargs))
        response.headers['Access-Control-Allow-Headers'] = 'Content-Type,auth_id,auth_key'
        if "origin" in request.headers:
            response.headers["Access-Control-Allow-Origin"] = request.headers["origin"]
        else:
            response.headers["Access-Control-Allow-Origin"] = "*"
        return response
    return decorator

# 用于缓存 api 密钥
auth_cache = {}

def require_authorize(function):
    '''
    鉴权装饰器
    ->
        @function 被装饰的函数
        @request.headers.auth_id
        @request.headers.auth_key
    <-
        function(*args, **kwargs)
        
    '''
    @functools.wraps(function)
    def decoration(*args, **kwargs):
        if ("auth_id" in request.headers) and ("auth_key" in request.headers):
            if request.headers.get("auth_key") == auth_cache.get(request.headers.get("auth_id")):
                return function(*args, **kwargs)
            else:
                return "",403
        else:
            return "",400
    return decoration


@route_of_admin.route("/auth", methods=["OPTIONS","POST"])
@cross_origin
def request_auth():
    '''
    认证
    ->
        @mail request.form.mail
        @password request.form.password
    <-
        auth_id Int
        auth_key String
    '''

    if ('mail' in request.form) and ('password' in request.form):
        input_mail = request.form['mail']
        input_password = request.form['password']
        
        if (input_mail == CONFIG.config_cache.get("admin_mail")) and (
                to_md5(raw=input_password,mix_text=CONFIG.config_cache.get("admin_salt")) == CONFIG.config_cache.get("admin_password")):
            # 创建认证缓存，
            # auth_id 为当前 13 位时间戳
            # auth_key 为邮箱+密码哈希+auth_id的MD5

            auth_id = str(timestamp_13())
            auth_key = to_md5(CONFIG.config_cache.get("admin_mail")+CONFIG.config_cache.get("admin_password")+auth_id)
            
            # 创建密钥缓存，但只允许存在一对密钥
            auth_cache.clear()
            auth_cache[auth_id] = auth_key
    
            return jsonify({
                "auth_id":auth_id,
                "auth_key":auth_key
            })

        else:
            return "",401
    else:
        return "",400
    

@route_of_admin.route("/app", methods=["OPTIONS","POST"])
@cross_origin
@require_authorize
def view_add_app_post(*args,**kwargs):

    Log.write("尝试安装应用 <- {}".format(str([request.form,request.files])) )
    
    app_name = request.form.get("n")
    app_mode = request.form.get("m")
    app_package = request.files.get("file")

    if not app_name: return jsonify({
        "errmsg":"请填写应用名称"
    }),400
    if not (1 < len(str(app_name)) < 20):return jsonify({
        "errmsg":"应用名称的长度为[2,19]"
    }),400
    if not app_mode in ["0","1"]:return jsonify({
        "errmsg":"应用发布模式不合法"
    }),400
    if not app_package:return jsonify({
        "errmsg":"缺少应用包"
    }),400
    
    app_id = str(timestamp_13())
    app_package_dir = os.path.join(WK_PATH,"archive","app_package")
    if not os.path.isdir(app_package_dir): os.makedirs(app_package_dir)
    with open(os.path.join(app_package_dir,app_id),"wb")as t:
        t.write(app_package.read())
        Log.write("{} 的应用包已被存储到  {}".format(app_name,os.path.join(WK_PATH,"archive","app_package",app_id)))
        
    if app_mode == "0":
        app_host = "0.0.0.0"
    else:
        app_domain = request.form.get("d")
        if not is_domain(raw=app_domain):return jsonify({
            "errmsg":"应用域名不正确"
        }),400
        if app_domain in str(CONFIG.config_cache):return jsonify({
            "errmsg":"域名已被占用"
        }),400
        app_host = app_domain
        Log.write("{} 引起 caddyserver 配置重写".format(app_name))
        Caddy_config.rebuild()

    
    app_port = CONFIG.get_a_free_port()
    app_work_path = os.path.join(WK_PATH,"apps",app_id)

    CONFIG.add_app(app_id=app_id,app_mode=app_mode,app_name=app_name,app_status=0,app_host=app_host,app_run_port=app_port,app_run_workers=1,app_work_path=app_work_path)
    Log.write("{} 已被记录".format(app_name))
    Log.write("{} 开始进行部署".format(app_name))

    Q_01.put({
        "target":App.new,
        "args":[],
        "kwargs":{
            "app_id":app_id
        }
    })
    # App.new(app_id=app_id)

    return "",201


@route_of_admin.route("/app/<app_id>", methods=["OPTIONS","GET"])
@cross_origin
@require_authorize
def view_app_get_info(*args,**kwargs):
    app_id = kwargs.get("app_id")
    if not app_id:return "",400
    return jsonify(CONFIG.config_cache.apps.get(app_id))


@route_of_admin.route("/app/<app_id>/start", methods=["OPTIONS","GET"])
@cross_origin
@require_authorize
def view_app_start(*args,**kwargs):
    app_id = kwargs.get("app_id")
    if not app_id:return "",400
    App.start_app(app_id=app_id)
    return ""

@route_of_admin.route("/app/<app_id>/delete", methods=["OPTIONS","GET"])
@cross_origin
@require_authorize
def view_app_delete(*args,**kwargs):
    app_id = kwargs.get("app_id")
    if not app_id:return "",400
    App.delete_app(app_id=app_id)
    return ""

@route_of_admin.route("/app/<app_id>/stop", methods=["OPTIONS","GET"])
@cross_origin
@require_authorize
def view_app_stop(*args,**kwargs):
    app_id = kwargs.get("app_id")
    if not app_id:return "",400
    App.stop_app(app_id=app_id)
    return ""


@route_of_admin.route("/app/<app_id>/restart", methods=["OPTIONS","GET"])
@cross_origin
@require_authorize
def view_app_restart(*args,**kwargs):
    app_id = kwargs.get("app_id")
    if not app_id:return "",400
    App.restart_app(app_id=app_id)
    return ""


@route_of_admin.route("/app/<app_id>/enable", methods=["OPTIONS","GET"])
@cross_origin
@require_authorize
def view_app_enable(*args,**kwargs):
    app_id = kwargs.get("app_id")
    if not app_id:return "",400
    App.enable_app(app_id=app_id)
    return ""


@route_of_admin.route("/app/<app_id>/disable", methods=["OPTIONS","GET"])
@cross_origin
@require_authorize
def view_app_disable(*args,**kwargs):
    app_id = kwargs.get("app_id")
    if not app_id:return "",400
    App.disable_app(app_id=app_id)
    return ""


@route_of_admin.route("/app/<app_id>/unit_status", methods=["OPTIONS","GET"])
@cross_origin
@require_authorize
def view_app_get_unit_status(*args,**kwargs):
    app_id = kwargs.get("app_id")
    if not app_id:return "",400
    return jsonify(App.app_unit_status(app_id=app_id))

@route_of_admin.route("/restart_caddy", methods=["OPTIONS","GET"])
@cross_origin
@require_authorize
def view_reload_caddy(*args,**kwargs):
    r = shell("systemctl restart caddy_bin")
    return r[1].split("\n")


@route_of_admin.route("/config", methods=["OPTIONS","GET"])
@cross_origin
@require_authorize
def view_config(*args,**kwargs):
    return jsonify(CONFIG.config_cache)


@route_of_admin.route("/log", methods=["OPTIONS","GET"])
@cross_origin
@require_authorize
def view_log(*args,**kwargs):
    app_id = request.args.get("app_id")
    if app_id:
        d = Log.read(app_id=app_id)
    else:
        d = Log.read()
    return jsonify(d)

@route_of_admin.route("/thread_status", methods=["OPTIONS","GET"])
@cross_origin
@require_authorize
def view_thread_status(*args,**kwargs):

    return jsonify(F_01_status)


@route_of_admin.route("/test", methods=["OPTIONS","GET"])
@cross_origin
@require_authorize
def view_test(*args,**kwargs):
        
    r = Caddy_config.rebuild()
    return jsonify(r)